using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Net;
using System.Xml;
using System.Text.RegularExpressions;

namespace Dimok.Web {

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Http. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public partial class HTTP {

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Exception for signalling malformed zip errors. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public class MalformedZipException : ApplicationException { }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Exception for signalling malformed phone errors. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public class MalformedPhoneException : ApplicationException { }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Values that represent ZipType. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private enum ZipType { US, CA, UnKnown };

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Normalize zip. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="zip">	The zip. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private static string NormalizeZip(string zip) { return Regex.Replace(zip, "\\s", ""); }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Normalize phone. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="phone">	The phone. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private static string NormalizePhone(string phone) { return Regex.Replace(phone, "\\D", ""); }
		/// <summary> The zip pattern us </summary>
		public const string ZIP_PATTERN_US = "\\d{5}";
		public const string ZIP_PATTERN_CA = @"[a-z]\d[a-z]\d[a-z]\d";
		private static ZipType GetZipType(string zip) {
			zip = NormalizeZip(zip);
			if (Regex.Match(zip, "^"+ZIP_PATTERN_US, RegexOptions.Compiled).Success ) return ZipType.US;
			if (Regex.Match(zip, ZIP_PATTERN_CA, RegexOptions.IgnoreCase | RegexOptions.Compiled).Success) return ZipType.CA;
			return ZipType.UnKnown;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Check zip type. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="zip">			The zip. </param>
		/// <param name="zipType">	Type of the zip. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private static string CheckZipType(string zip,ZipType zipType) {
			if (GetZipType(zip) == ZipType.US) return NormalizeZip(zip);
			if (GetZipType(zip) == ZipType.CA) return NormalizeZip(zip);
			return "";
		}
		private delegate PostalDS.StreetDataTable CallWhitePages(string nameFirst, string nameLast, string zip);
		private delegate PostalDS.StreetDataTable CallWhitePagesByPhone(string phone);
		private delegate PostalDS.StreetDataTable CallSwitchBoard(string name, string zip);
		private delegate PostalDS CallStreetByZip(string zip);

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Empty street table. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="zip">		The zip. </param>
		/// <param name="street">	The street. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected static PostalDS.StreetDataTable EmptyStreetTable(string zip, string street) {
			PostalDS.StreetDataTable dt = new PostalDS().Street;
			dt.AddStreetRow("", "", "", "", street, "", "", zip, "");
			return dt;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Verify street. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="zip">			The zip. </param>
		/// <param name="address">	The address. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static PostalDS.StreetDataTable VerifyStreet(string zip, string address) {
			zip = NormalizeZip(zip);
			switch (GetZipType(zip)) {
				case ZipType.US:
					return US.Melissa.VerifyStreet(zip, address);
				case ZipType.CA:
					return US.Melissa.VerifyStreet(zip, address);
				default:
					return EmptyStreetTable(zip, address);
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Street by zip. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="MalformedZipException">	Thrown when malformedzip. </exception>
		///
		/// <param name="zip">				The zip. </param>
		/// <param name="nameFirst">	The name first. </param>
		/// <param name="nameLast">		The name last. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static PostalDS StreetByZip(string zip,string nameFirst,string nameLast) {
			zip = NormalizeZip(zip);
			switch (GetZipType(zip)) {
				case ZipType.US:

					CallStreetByZip callSBZ = new CallStreetByZip(US.StreetByZip);
					IAsyncResult resSBZ = callSBZ.BeginInvoke(zip, null, null);

					//CallWhitePages callWP = new CallWhitePages(US.WhitePages);
					//IAsyncResult resWP = callWP.BeginInvoke(nameFirst, nameLast, zip, null, null);

					//CallSwitchBoard callSB = new CallSwitchBoard(US.SwitchBoard);
					//IAsyncResult resSB = callSB.BeginInvoke(nameFirst+" "+nameLast, zip, null, null);

					#region EndInvoke
					//PostalDS.StreetDataTable dtStreet = callSB.EndInvoke(resSB);
					//PostalDS.StreetDataTable dtWP = callWP.EndInvoke(resWP);
					PostalDS dsPostal = callSBZ.EndInvoke(resSBZ);
					#endregion

					//dsPostal.Merge(dtStreet);
					//dsPostal.Merge(dtWP);
					//dtStreet.Merge(dsPostal.Street);
					//dtStreet.Merge(dtWP);
					//dsPostal.Street.Clear();
					return dsPostal;
				case ZipType.CA:
					return CA.StreetByZip(zip);
				default:
					throw new MalformedZipException();
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Street by zip and house number. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="zip">					The zip. </param>
		/// <param name="houseNumber">	The house number. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static PostalDS StreetByZipAndHouseNumber(string zip, string houseNumber) {
			return US.StreetByZipAndHouseNumber(zip, houseNumber);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Street by phone. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="phone">	The phone. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static PostalDS.StreetDataTable StreetByPhone(string phone) {
			phone = NormalizePhone(phone);
			return US.Google(phone);
			//CallWhitePagesByPhone callWPByPhone = new CallWhitePagesByPhone(US.WhitePages);
			//IAsyncResult resWPByPhone = callWPByPhone.BeginInvoke(phone, null, null);
			//return callWPByPhone.EndInvoke(resWPByPhone);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Searches for the first phones. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="firstName">	Name of the first. </param>
		/// <param name="lastName">		Name of the last. </param>
		/// <param name="zip">				The zip. </param>
		///
		/// <returns>	The found phones. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string[] FindPhones(string firstName, string lastName, string zip) {
			return US.FindPhones(firstName, lastName, zip);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets a company by upc. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="upc">	The upc. </param>
		///
		/// <returns>	The company by upc. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string GetCompanyByUPC(string upc) {
			WebClient wc = new WebClient();
			string html = wc.UploadString("http://directory.gs1.org/gtin/search","POST","queryByGTIN[type]=party&queryByGTIN[key]=" + upc);
			string result = Regex.Match(html,"<!-- RESULT START -->([\\s\\S]+)<!-- RESULT END -->").Groups[1].Value;
			XmlDocument doc = new System.Xml.XmlDocument();
			doc.LoadXml(result);
			XmlNode node = doc.SelectSingleNode("//strong");
			return node == null ? "" : node.InnerText;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Google search. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="pattern">	The pattern. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static DS.GoogleSearch GoogleSearch(string pattern) {
			WebClient wc = new WebClient();
			string html = wc.DownloadString("http://www.google.com/search?as_q=" + pattern + "&hl=en&num=10");
			MatchCollection mc = Regex.Matches(html,@"<h2[\s\S]*?</h2>");
			DS.GoogleSearch dsDS = new DS.GoogleSearch();
			DS.GoogleSearch.ResultsDataTable dt = dsDS.Results;
			foreach(Match m in mc) {
				XmlDocument doc = new XmlDocument();
				string header = Regex.Replace(m.Value,@"(<[^/\s>]+)[^>]*?>","$1>").Replace("<br>","<br/>");
				try {
					doc.LoadXml(header);
				} catch { continue; }
				string text = Regex.Replace(doc.InnerText,"(\\W)(\\W)","$1");
				string search = Regex.Replace(Regex.Replace(doc.InnerText,"(\\d+)"," $1 "),"(\\W)(\\W)","$1");
				dt.AddResultsRow(text,search,0);
			}
			foreach(DS.GoogleSearch.ResultsRow dr in dt)
				foreach(DS.GoogleSearch.ResultsRow r in dt)
					if(r != dr) {
						string s = "(" + Regex.Replace(r.Search + "","\\W","\\W|") + ")";
						int rate = Regex.Matches(dr.Search + "",s,RegexOptions.IgnoreCase).Count;
						dr.Rate += rate;
					}
			return SortTable(dt,"Rate DESC") as DS.GoogleSearch;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Sort table. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="dataTable">	The data table. </param>
		/// <param name="sortBy">			Amount to sort by. </param>
		///
		/// <returns>	The sorted table. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static DataSet SortTable(DataTable dataTable, string sortBy) {
			if (dataTable.DataSet == null) new DataSet().Tables.Add(dataTable);
			DataSet dsSorted = dataTable.DataSet.Clone();
			dsSorted.Merge(dataTable.Select("", sortBy));
			dataTable.Rows.Clear();
			dataTable.DataSet.Merge(dsSorted.Tables[dataTable.TableName]);
			return dataTable.DataSet;
		}

	}
}
